package pl.polidea.imf.common.java.impl.storage;

import java.io.IOException;
import java.io.OutputStream;
import java.util.Enumeration;

import javax.microedition.io.Connector;
import javax.microedition.io.file.FileConnection;

import pl.polidea.imf.blackberry.util.FileConnectionHelper;
import pl.polidea.imf.blackberry.util.FileSystemHelper;
import pl.polidea.imf.blackberry.util.FileSystemHelper.FileSystemHelperException;
import pl.polidea.imf.common.java.errors.ErrorCode;
import pl.polidea.imf.common.java.json.JSONSerializable;
import pl.polidea.imf.common.java.json.JSONSerializable.JSONSerializationException;
import pl.polidea.imf.common.java.storage.JSONItemStorage;
import pl.polidea.imf.common.java.util.JSONHelper;
import pl.polidea.imf.common.java.util.StreamHelper;

public class FileJSONItemStorage implements JSONItemStorage {

	
	public static final String LOG_NAME__ = "_to_send";
	public static final String CURRENT_LOG_NAME_PREFIX = "current_";
	public static final String LOG_EXT = ".txt";

	public static final long LOG_SIZE_ = 20000l; // 20 * 1000;
	public static final int ITEMS_FOR_FILE = 50;

	private String storageDir;
	private String logItemName;
	private String currentLogItemName;
	private FileConnection currentLoggingFile;
	private OutputStream logOutputStream;

	private long bytesWrittenToCurrentLog;
	private int currentLogNumber;
	private int itemsInFile = 0;

	String logString;
	byte[] logBytes;

	public FileJSONItemStorage(String directory, String namePrefix) throws FileJSONItemStorageException {
		try {
			storageDir = directory;
			logItemName = namePrefix+LOG_NAME__;
			currentLogItemName = CURRENT_LOG_NAME_PREFIX + namePrefix;
			FileSystemHelper.ensureFolderExists(storageDir);
			currentLogNumber = findLastLogNumber();
			changeLogingFile();
			

		} catch (FileSystemHelperException e) {
			throw new FileJSONItemStorageException(FileJSONItemStorageException.CANNOT_CREATE_LOG_DIR, e);
		}

	}

	public synchronized void collectItem(JSONSerializable logItem) throws FileJSONItemStorageException {
		if (itemsInFile > ITEMS_FOR_FILE) {

			changeLogingFile();

			bytesWrittenToCurrentLog = 0;
			itemsInFile = 0;
		}
		try {
			if (itemsInFile != 0) {
				logOutputStream.write(JSONHelper.JSON_COMMA);
			}
			logString = logItem.toJSON().toString();
			logBytes = JSONHelper.getBytesForString(logString);
			logOutputStream.write(logBytes);
			bytesWrittenToCurrentLog += logBytes.length;
			itemsInFile++;

		} catch (IOException e) {
			throw new FileJSONItemStorageException(FileJSONItemStorageException.CANNOT_COLLECT_LOG, e);
		} catch (JSONSerializationException e) {
			throw new IllegalStateException("Cannot serialize JSON object");
		}

	}

	private synchronized void changeLogingFile() throws FileJSONItemStorageException {
		
		StreamHelper.closeOutputStream(logOutputStream);
		try {
			if (currentLoggingFile != null) {
				currentLogNumber++;
				currentLoggingFile.rename(logItemName + currentLogNumber + LOG_EXT);
			}
		} catch (IOException e) {
			throw new FileJSONItemStorageException(FileJSONItemStorageException.CANNOT_CHANGE_LOGFILE, e);
		}
		FileConnectionHelper.closeConnection(currentLoggingFile);

		try {
			currentLoggingFile = (FileConnection) Connector.open(storageDir + currentLogItemName + LOG_EXT, Connector.READ_WRITE);
			if (!currentLoggingFile.exists()) {
				currentLoggingFile.create();

			} else {
				
				currentLogNumber++;
				currentLoggingFile.rename(logItemName + currentLogNumber + LOG_EXT);
				FileConnectionHelper.closeConnection(currentLoggingFile);
				currentLoggingFile = (FileConnection) Connector.open(storageDir + currentLogItemName + LOG_EXT, Connector.READ_WRITE);
				currentLoggingFile.create();
			}

		} catch (IOException e) {
			throw new FileJSONItemStorageException(FileJSONItemStorageException.CANNOT_CHANGE_LOGFILE, e);
		}
		try {
			logOutputStream = currentLoggingFile.openOutputStream();
			
		} catch (IOException e) {
			throw new FileJSONItemStorageException(FileJSONItemStorageException.CANNOT_CHANGE_LOGFILE, e);
		}
	}

	public synchronized Enumeration getLogStreams() throws IOException {
		FileConnection logDir = null;
		try {
			logDir = (FileConnection) Connector.open(storageDir, Connector.READ);
			Enumeration fileList = logDir.list(logItemName + "*" + LOG_EXT, true);
			return new FileLogStreamEnumeration(storageDir, fileList);
		} finally {
			FileConnectionHelper.closeConnection(logDir);
		}

	}

	private int findLastLogNumber() {
		int result = Integer.MIN_VALUE;
		FileConnection logDir = null;
		try {
			int indexOfLogName = -1;
			int indexOfLogExt = -1;

			String fileName;
			String numberString;

			int number;

			logDir = (FileConnection) Connector.open(storageDir, Connector.READ);

			Enumeration fileList = logDir.list(logItemName + "*" + LOG_EXT, true);

			while (fileList.hasMoreElements()) {
				fileName = (String) fileList.nextElement();
				indexOfLogName = fileName.indexOf(logItemName);
				indexOfLogExt = fileName.indexOf(LOG_EXT);
				numberString = fileName.substring(indexOfLogName + logItemName.length(), indexOfLogExt);
				try {
					number = Integer.parseInt(numberString);
					if (number > result) {
						result = number;
					}
				} catch (Exception e) {

				}

			}

		} catch (IOException e) {
			result = 0;
		} finally {
			FileConnectionHelper.closeConnection(logDir);
		}
		if (result == Integer.MIN_VALUE) {
			result = 0;
		}
		return result;
	}

	public static class FileJSONItemStorageException extends JSONItemStorageException {

		public FileJSONItemStorageException(ErrorCode code) {
			this(code, null, null);

		}

		public FileJSONItemStorageException(ErrorCode code, Throwable t) {
			this(code, null, t);

		}

		public FileJSONItemStorageException(ErrorCode code, String msg, Throwable cause) {
			super(code, msg, cause);

		}

		static final ErrorCode CANNOT_CREATE_LOG_DIR = new ErrorCode(0);
		static final ErrorCode CANNOT_CHANGE_LOGFILE = new ErrorCode(1);
		static final ErrorCode CANNOT_COLLECT_LOG = new ErrorCode(2);

	}

	

	

}
